package com.fyakm.daodejing.stock.service.impl;


import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import com.fyakm.daodejing.stock.domain.StockHistoryItem;
import com.fyakm.daodejing.stock.service.StockTradingStrategy;

/**
 * 简单移动平均线交叉策略类，实现了 StockTradingStrategy 接口。
 * 该策略通过比较短期和长期简单移动平均线（SMA）的交叉情况来生成交易信号，
 * 并根据预设的买卖数量给出具体的交易建议。
 */
public class MovingAverageCrossStrategy implements StockTradingStrategy {
    // 短期移动平均线的计算周期
    private int shortPeriod;
    // 长期移动平均线的计算周期
    private int longPeriod;
    // 当出现买入信号时建议买入的股票数量
    private BigDecimal buyQuantity;
    // 当出现卖出信号时建议卖出的股票数量
    private BigDecimal sellQuantity;

    /**
     * 构造函数，用于初始化策略的参数。
     *
     * @param shortPeriod  短期移动平均线的计算周期
     * @param longPeriod   长期移动平均线的计算周期
     * @param buyQuantity  买入股票的数量
     * @param sellQuantity 卖出股票的数量
     */
    public MovingAverageCrossStrategy(int shortPeriod, int longPeriod, BigDecimal buyQuantity, BigDecimal sellQuantity) {
        this.shortPeriod = shortPeriod;
        this.longPeriod = longPeriod;
        this.buyQuantity = buyQuantity;
        this.sellQuantity = sellQuantity;
    }

    /**
     * 执行简单移动平均线交叉策略，根据历史数据计算短期和长期移动平均线，
     * 并根据它们的交叉情况生成交易信号，返回相应的交易数量。
     * 正数表示买入，负数表示卖出，0 表示无交易信号。
     *
     * @param historyItems 股票历史数据列表
     * @return 交易数量
     */
    @Override
    public BigDecimal executeStrategy(List<StockHistoryItem> historyItems) {
        // 如果历史数据的数量少于长期移动平均线的计算周期，无法进行有效计算，返回无交易信号
        if (historyItems.size() < longPeriod) {
            return BigDecimal.ZERO;
        }

        // 计算短期移动平均线
        List<BigDecimal> shortSMA = calculateSMA(historyItems, shortPeriod);
        // 计算长期移动平均线
        List<BigDecimal> longSMA = calculateSMA(historyItems, longPeriod);

        // 获取历史数据的最后一个索引
        int lastIndex = historyItems.size() - 1;
        // 如果最后一个索引小于长期移动平均线的计算周期，无法进行有效判断，返回无交易信号
        if (lastIndex < longPeriod) {
            return BigDecimal.ZERO;
        }

        // 获取上一个交易日的短期移动平均线值
        BigDecimal prevShortSMA = shortSMA.get(lastIndex - 1);
        // 获取上一个交易日的长期移动平均线值
        BigDecimal prevLongSMA = longSMA.get(lastIndex - 1);
        // 获取当前交易日的短期移动平均线值
        BigDecimal currentShortSMA = shortSMA.get(lastIndex);
        // 获取当前交易日的长期移动平均线值
        BigDecimal currentLongSMA = longSMA.get(lastIndex);

        // 如果上一个交易日短期均线小于等于长期均线，且当前交易日短期均线大于长期均线，产生买入信号
        if (prevShortSMA.compareTo(prevLongSMA) <= 0 && currentShortSMA.compareTo(currentLongSMA) > 0) {
            // 输出买入信息
            System.out.println("Buy signal at " + historyItems.get(lastIndex).getDate());
            return buyQuantity;
        }
        // 如果上一个交易日短期均线大于等于长期均线，且当前交易日短期均线小于长期均线，产生卖出信号
        else if (prevShortSMA.compareTo(prevLongSMA) >= 0 && currentShortSMA.compareTo(currentLongSMA) < 0) {
            // 输出卖出信息
            System.out.println("Sell signal at " + historyItems.get(lastIndex).getDate());
            return sellQuantity.negate();
        }
        // 其他情况，无交易信号
        else {
            return BigDecimal.ZERO;
        }
    }

    /**
     * 计算指定周期的简单移动平均线（SMA）。
     *
     * @param historyItems 股票历史数据列表
     * @param period       移动平均线的计算周期
     * @return 包含每个交易日对应移动平均线值的列表
     */
    private List<BigDecimal> calculateSMA(List<StockHistoryItem> historyItems, int period) {
        // 用于存储计算得到的移动平均线值
        List<BigDecimal> sma = new ArrayList<>();
        // 遍历历史数据
        for (int i = 0; i < historyItems.size(); i++) {
            // 如果当前索引小于计算周期减 1，无法计算完整的移动平均线，添加 null 占位
            if (i < period - 1) {
                sma.add(null);
                continue;
            }
            // 初始化求和变量
            BigDecimal sum = BigDecimal.ZERO;
            // 计算从当前索引往前 period 个交易日的收盘价总和
            for (int j = i - period + 1; j <= i; j++) {
                sum = sum.add(historyItems.get(j).getClose());
            }
            // 计算移动平均线值，即总和除以周期
            sma.add(sum.divide(BigDecimal.valueOf(period)));
        }
        return sma;
    }
}
